use std::num::NonZeroUsize;
use std::thread;
use std::{io, net::TcpListener as StdTcpListener};

use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};

use crate::server::ServerCommand;
use crate::service::InternalServiceFactory;
use crate::socket::MioListener;
use crate::worker::ServerWorkerConfig;
use crate::Server;

pub struct ServerBuilder {
    pub(crate) token: usize,
    pub(crate) threads: usize,
    pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
    pub(crate) sockets: Vec<(usize, String, MioListener)>,
    pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
    pub(crate) cmd_rx: UnboundedReceiver<ServerCommand>,
    pub(crate) worker_config: ServerWorkerConfig,
    pub(crate) listen_os_signals: bool,
    pub(crate) exit: bool,
}

impl Default for ServerBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl ServerBuilder {
    /// 初始化
    fn new() -> ServerBuilder {
        let (cmd_tx, cmd_rx) = unbounded_channel();
        ServerBuilder {
            token: 0,
            threads: thread::available_parallelism().map_or(2, NonZeroUsize::get),
            factories: Vec::new(),
            sockets: Vec::new(),
            cmd_tx,
            cmd_rx,
            worker_config: ServerWorkerConfig::default(),
            listen_os_signals: true,
            exit: false,
        }
    }

    /// 监听
    pub fn listen<N: AsRef<str>>(mut self, name: N, lst: StdTcpListener) -> io::Result<Self> {
        let str = String::from("x");
        lst.set_nonblocking(true)?;
        let addr = lst.local_addr()?;
        let token = self.next_token();
        /// TODO: factory | sockets
        self.sockets
            .push((token, name.as_ref().to_string(), MioListener::from(lst)));
        Ok(self)
    }

    /// 开始处理连接请求，并返回 Server 控制
    pub fn run(self) -> Server {
        if self.sockets.is_empty() {
            panic!("Server should have at least one bound socket")
        } else {
            Server::new(self)
        }
    }

    /// 获取token,并累加token
    fn next_token(&mut self) -> usize {
        let token = self.token;
        self.token += 1;
        token
    }
}
